home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / exampleCode / games / IndiZone / sw / ship.c++ < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-02  |  53.8 KB  |  1,900 lines

  1. /*
  2.  * Copyright (C) 1994, Silicon Graphics, Inc.
  3.  * All Rights Reserved.
  4.  *
  5.  * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
  6.  * the contents of this file may not be disclosed to third parties, copied or
  7.  * duplicated in any form, in whole or in part, without the prior written
  8.  * permission of Silicon Graphics, Inc.
  9.  *
  10.  * RESTRICTED RIGHTS LEGEND:
  11.  * Use, duplication or disclosure by the Government is subject to restrictions
  12.  * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
  13.  * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
  14.  * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
  15.  * rights reserved under the Copyright Laws of the United States.
  16.  */
  17. #include <string.h>
  18. #include "sw.h"
  19. #include "extern.h"
  20. #include "ship.h"
  21. #include "main.h"
  22. #include "control.h"
  23. #include "universe.h"
  24. #include "sw_comm.h"
  25. #include "sound.h"
  26. #include <Inventor/nodes/SoSeparator.h>
  27. #include <Inventor/nodes/SoTranslation.h>
  28. #include <Inventor/nodes/SoRotation.h>
  29. #include <Inventor/nodes/SoTransform.h>
  30. #include <Inventor/nodes/SoSwitch.h>
  31. #include <Inventor/nodes/SoMaterial.h>
  32. #include <Inventor/nodes/SoGroup.h>
  33. #include <Inventor/nodes/SoScale.h>
  34. #include <Inventor/nodes/SoCoordinate3.h>
  35. #include <Inventor/nodes/SoMaterialBinding.h>
  36. #include <Inventor/nodes/SoLightModel.h>
  37. #include <Inventor/nodes/SoComplexity.h>
  38. #include <Inventor/nodes/SoSphere.h>
  39. #include <Inventor/nodes/SoCylinder.h>
  40. #include <Inventor/nodes/SoCone.h>
  41. #include <Inventor/nodes/SoTriangleStripSet.h>
  42. #include <Inventor/nodes/SoQuadMesh.h>
  43. #include <Inventor/actions/SoGetBoundingBoxAction.h>
  44.  
  45.  
  46. #define    DEFAULT_MASS        50000        // kg
  47. #define    DEFAULT_ANGACCEL    (M_PI/3.0)    // rad/s/s
  48. #define    DEFAULT_RESPONSE    0.6        // frac / second
  49. #define    DEFAULT_MAXSHIELD    100000        // Joules
  50. #define    DEFAULT_SHIELDRECOVERY    0 /*2000*/    // J/s
  51. #define    DEFAULT_LASER_POWER    30000        // J/s
  52. #define    DEFAULT_MISSILE_ENERGY    35000        // J
  53. #define    DEFAULT_MAXMISSILES    20
  54. #define    DEFAULT_MAXFORCE    (100.0*50000.0)    // N
  55. #define    DEFAULT_FUELRATE    0.01
  56.  
  57. #define    MAXTRACKACC    (M_PI/3.0)        // rad/s
  58. #define    ANGVELMULT    3.0
  59. #define    DISSIPATE_RATE    1.0            // shield shade dissipate rate
  60. #define    MISSILE_EXPLODE    1.5            // missile explosion time
  61. #define    SHIP_EXPLODE    4.0            // ship explode time
  62. #define    SOLARTHRUST    0.04
  63. #define    MISSILE_ACCEL    196.0
  64. #define    MISSILE_ANG    (M_PI/2.0)
  65. #define    MISSILE_TIME    (12.0 + MISSILE_EXPLODE)
  66. #define    MISSILE_SIZE    3.0
  67. #define    MISSILE_MASS    1000
  68. #define    GLIDE_TIME    1.2
  69.  
  70. #define    SHIELDSIZE    1.75
  71. #define    HITSPREAD    (M_PI/3.0)
  72. #define    HITRES        2
  73. #define    HITSIZE        (2*HITRES+1)
  74. #define    HITPOINTS    (HITSIZE * HITSIZE)
  75.  
  76. static SbRotation    zeroAngularVelocity = SbRotation::identity();
  77. static SbVec3f        straightAhead(0.0, 0.0, -1.0);
  78.  
  79. //
  80. // ShipObject
  81. //
  82.  
  83. SoSeparator*        ShipObject::missileGeom = NULL;
  84.  
  85. ShipObject::ShipObject(NetId id, Team team, const char* name)
  86. {
  87.   wholeShip = new SoSeparator;
  88.   wholeShip->ref();
  89.   wholeShip->addChild(activeSwitch = new SoSwitch);
  90.  
  91.   // set constants
  92.   info.myId = id;
  93.   myTeam = team;
  94.   myName = strdup(name);
  95.  
  96.   // initialize score
  97.   info.won = 0;
  98.   info.lost = 0;
  99.  
  100.   // ship is not active yet
  101.   info.info.active = ObjectInactive;
  102.   activeSwitch->whichChild = SO_SWITCH_NONE;    // hide all
  103. }
  104.  
  105. ShipObject::~ShipObject()
  106. {
  107.   wholeShip->unref();
  108. }
  109.  
  110. void            ShipObject::init(SoSeparator* shipGeom,
  111.                     SoSeparator* shipChangeGeom,
  112.                     const SbVec3f& turretPos,
  113.                     const SbVec3f& flagPos,
  114.                     const SbVec3f& cockpitPos)
  115. {
  116.   cockpit = cockpitPos;
  117.  
  118.   // make laser turret stuff
  119.   laserLocation = new SoTranslation;
  120.   laserOrientation = new SoRotation;
  121.   laserLocation->translation.setValue(turretPos);
  122.  
  123.   // make missile
  124.   if (missileGeom == NULL) {
  125.     missileGeom = new SoSeparator;
  126.     missileGeom->ref();
  127.     missileGeom->setGLRenderCaching(TRUE);    // missiles don't change
  128.  
  129.     SoComplexity* missileCmplx = new SoComplexity;
  130.     missileGeom->addChild(missileCmplx);
  131.     missileCmplx->value = 0.2;
  132.     SoRotation* missileTurn = new SoRotation;
  133.     missileGeom->addChild(missileTurn);
  134.     missileTurn->rotation.setValue(SbRotation(SbVec3f(0.0, 1.0, 0.0),
  135.                         SbVec3f(0.0, 0.0, -1.0)));
  136.  
  137.     SoSeparator* nose = new SoSeparator;
  138.     nose->setGLRenderCaching(TRUE);
  139.     missileGeom->addChild(nose);
  140.     SoTranslation* nosePos = new SoTranslation;
  141.     nose->addChild(nosePos);
  142.     nosePos->translation.setValue(0.0, 3.0, 0.0);
  143.     SoCone* noseGeom = new SoCone;
  144.     nose->addChild(noseGeom);
  145.     noseGeom->height = 1.0;
  146.     noseGeom->bottomRadius = 0.5;
  147.     noseGeom->parts = SoCone::SIDES;
  148.  
  149.     SoSeparator* body = new SoSeparator;
  150.     body->setGLRenderCaching(TRUE);
  151.     missileGeom->addChild(body);
  152.     SoCylinder* bodyGeom = new SoCylinder;
  153.     body->addChild(bodyGeom);
  154.     bodyGeom->height = 5.0;
  155.     bodyGeom->radius = 0.5;
  156.     bodyGeom->parts = SoCylinder::SIDES | SoCylinder::BOTTOM;
  157.  
  158.     SoSeparator* engine = new SoSeparator;
  159.     engine->setGLRenderCaching(TRUE);
  160.     missileGeom->addChild(engine);
  161.     SoMaterial* engineMat = new SoMaterial;
  162.     engine->addChild(engineMat);
  163.     engineMat->ambientColor.setValue(0.0, 0.0, 0.0);
  164.     engineMat->diffuseColor.setValue(0.0, 0.0, 0.0);
  165.     engineMat->specularColor.setValue(0.0, 0.0, 0.0);
  166.     engineMat->emissiveColor.setValue(0.0, 1.0, 1.0);
  167.     engineMat->shininess = 0.0;
  168.     engineMat->transparency = 0.5;
  169.     SoTranslation* enginePos = new SoTranslation;
  170.     engine->addChild(enginePos);
  171.     enginePos->translation.setValue(0.0, -3.0, 0.0);
  172.     SoCone* engineGeom = new SoCone;
  173.     engine->addChild(engineGeom);
  174.     engineGeom->height = 2.0;
  175.     engineGeom->bottomRadius = 1.0;
  176.     engineGeom->parts = SoCone::SIDES;
  177.   }
  178.  
  179.   // make separator under active switch
  180.   SoSeparator* activeShip = new SoSeparator;
  181.   activeSwitch->addChild(activeShip);
  182.  
  183.   // ship stuff (i.e. all except missiles) under shipParts
  184.   SoSeparator* shipParts = new SoSeparator;
  185.   activeShip->addChild(shipParts);
  186.   shipParts->addChild(shipPosition = new SoTranslation);
  187.   shipParts->addChild(shipOrientation = new SoRotation);
  188.  
  189.   // put spacecraft geometry under switch
  190.   shipParts->addChild(shipSwitch = new SoSwitch);
  191.   SoGroup* shipGroup = new SoGroup;
  192.   shipSwitch->addChild(shipGroup);
  193.  
  194.   // add static ship parts (under switch)
  195.   shipGroup->addChild(shipGeom);
  196.   shipGeom->setGLRenderCaching(TRUE);        // cache static ship nodes
  197.  
  198.   // add dynamic ship parts (under switch)
  199.   shipGroup->addChild(shipChangeGeom);    
  200.   shipGroup->addChild(makeLaserTurret());
  201.  
  202.   // add laser beam under its own switch
  203.   shipParts->addChild(laserSwitch = new SoSwitch);
  204.   laserSwitch->addChild(makeLaserBeam());
  205.  
  206.   // add flag scene with a switch to choose which one to show
  207.   SoSeparator* flagSep = new SoSeparator;
  208.   shipParts->addChild(flagSep);
  209.   SoTranslation* flagLoc = new SoTranslation;
  210.   flagSep->addChild(flagLoc);
  211.   flagLoc->translation.setValue(flagPos);
  212.   flagPosition = flagPos;
  213.   flagSep->addChild(flagSwitch = new SoSwitch);
  214.   for (int i = 0; i < NUMTEAMS; i++)
  215.     flagSwitch->addChild(flagGeometry(Team(i)));
  216.   flagSwitch->whichChild = SO_SWITCH_NONE;
  217.  
  218.   // get ship size
  219.   SoGetBoundingBoxAction bboxAction;
  220.   bboxAction.apply(shipGeom);
  221.   SbVec3f c = bboxAction.getBoundingBox().getCenter();
  222.   sx = c[0];
  223.   sy = c[1];
  224.   sz = c[2];
  225.   bboxAction.getBoundingBox().getSize(sdx, sdy, sdz);
  226. //  sdx *= 0.5;
  227. //  sdy *= 0.5;
  228. //  sdz *= 0.5;
  229.   sbs = (sdx > sdy) ? sdx : sdy;
  230.   if (sdz > sbs) sbs = sdz;
  231.   sbs *= SHIELDSIZE;
  232.  
  233.   // add shield hits geometry
  234.   shipParts->addChild(makeShields());
  235.  
  236.   // make missiles
  237.   SoSeparator* allMissiles = new SoSeparator;
  238.   activeShip->addChild(allMissiles);
  239.  
  240.   for (i = 0; i < MAXMISSILES; i++) {
  241.     allMissiles->addChild(missileSwitch[i] = new SoSwitch);
  242.     SoSeparator* missileStuff = new SoSeparator;
  243.     missileSwitch[i]->addChild(missileStuff);
  244.     missileStuff->addChild(missilePosition[i] = new SoTranslation);
  245.     missileStuff->addChild(missileOrientation[i] = new SoRotation);
  246.     missileStuff->addChild(missileGeom);
  247.   }
  248.  
  249.   // get missile size
  250.   bboxAction.apply(missileGeom);
  251.   bboxAction.getBoundingBox().getSize(mdx, mdy, mdz);
  252.   mdx *= 0.5;
  253.   mdy *= 0.5;
  254.   mdz *= 0.5;
  255.   mbs = (mdx > mdy) ? mdx : mdy;
  256.   if (mdz > mbs) mbs = mdz;
  257. }
  258.  
  259. Team            ShipObject::team() const
  260. {
  261.   return myTeam;
  262. }
  263.  
  264. char*            ShipObject::name() const
  265. {
  266.   return myName;
  267. }
  268.  
  269. int            ShipObject::score() const
  270. {
  271.   return info.won - info.lost;
  272. }
  273.  
  274. int            ShipObject::won() const
  275. {
  276.   return info.won;
  277. }
  278.  
  279. int            ShipObject::lost() const
  280. {
  281.   return info.lost;
  282. }
  283.  
  284. Team            ShipObject::flag() const
  285. {
  286.   return info.flag;
  287. }
  288.  
  289. void            ShipObject::flag(Team t)
  290. {
  291. //  if (info.flag == t) return;            // already have it
  292.   info.flag = t;
  293.   if (t == NoTeam)
  294.     flagSwitch->whichChild = SO_SWITCH_NONE;
  295.   else
  296.     flagSwitch->whichChild = int(t);
  297. }
  298.  
  299. NetId            ShipObject::id() const
  300. {
  301. // CHANGED 08/11/93:
  302. //  return NetId(info.myId);
  303.   return NetId((InAddr&)info.myId);
  304. }
  305.  
  306. SoSeparator*        ShipObject::root() const
  307. {
  308.   return wholeShip;
  309. }
  310.  
  311. void            ShipObject::reset()
  312. {
  313.   int i;
  314.  
  315.   shipVisible = -1;                // don't know if visible
  316.   if (self(this)) {
  317.     shipVisibility(FALSE);            // hide ship
  318.     camera->aspectRatio = ASPECT;
  319.     camera->heightAngle = HEIGHTANGLE;
  320.     camera->nearDistance = HITHERPLANE;
  321.     camera->farDistance = YONPLANE;
  322.     camera->focalDistance = 50.0;
  323.     camera->viewportMapping = SoCamera::ADJUST_CAMERA;
  324.   }
  325.   else {
  326.     shipSwitch->whichChild = SO_SWITCH_ALL;    // show ship
  327.   }
  328.  
  329.   // ship is active
  330.   active(ObjectActive);
  331.  
  332.   // no flag yet
  333.   info.flag = RedTeam;                // force an update
  334.   flag(NoTeam);
  335.  
  336.   // set ship mass field
  337.   info.info.mass = mass();
  338.  
  339.   // fill 'er up
  340.   fuel = 1.0;
  341.  
  342.   // initialize position
  343.   info.info.position[0] = 0.0;
  344.   info.info.position[1] = 0.0;
  345.   info.info.position[2] = 0.0;
  346.  
  347.   // initialize velocity
  348.   info.info.velocity[0] = 0.0;
  349.   info.info.velocity[1] = 0.0;
  350.   info.info.velocity[2] = 0.0;
  351.  
  352.   // initialize orientation
  353.   shipOrientation->rotation = SbRotation::identity();
  354.  
  355.   // initialize angular velocity and target angular velocity
  356.   angularVelDir = straightAhead;
  357.   angularVelDirTarget = straightAhead;
  358.  
  359.   // initialize tracking direction and target tracking direction
  360.   trackingDirection = straightAhead;
  361.   trackingDirTarget = straightAhead;
  362.  
  363.   // not exploding
  364.   info.info.explodeTime = 0.0;
  365.  
  366.   // engines off
  367.   info.info.engineOutput = 0.0;
  368.   engineOutputTarget = 0.0;
  369.  
  370.   // can grab any flag
  371.   for (i = 0; i < NUMTEAMS; i++)
  372.     cantGrabFlag[i] = FALSE;
  373.  
  374.   // not firing anything
  375.   info.firingLaser = FALSE;
  376.   laserTemp = 0.0;
  377.   laserSwitch->whichChild = SO_SWITCH_NONE;
  378.   missilesLeft = maxMissiles();
  379.   nextSilo = 0;
  380.   for (i = 0; i < MAXMISSILES; i++) {
  381.     missileTarget[i] = NULL;
  382.     info.missile[i].active = ObjectInactive;
  383.     missileVisible[i] = -1;
  384.     missileVisibility(i, FALSE);
  385.     missileTime[i] = 0.0;
  386.   }
  387.  
  388.   // initialize shields
  389.   for (i = int(FrontShield); i <= int(BottomShield); i++) {
  390.     info.shieldStrength[i] = 1.0;
  391.     info.shieldHits[i][0] = 0.0;
  392.   }
  393.  
  394.   // initialize shield hits
  395.   for (i = 0; i < MAXHITS; i++) {
  396.     hitSwitch[i]->whichChild = SO_SWITCH_NONE;
  397.     float *t = hitMaterial[i]->transparency.startEditing();
  398.     for (int j = 0; j < HITPOINTS; j++) t[j] = 1.0;
  399.     hitMaterial[i]->transparency.finishEditing();
  400.   }
  401.  
  402.   // fill out rest of information from what we've got now
  403.   advance(0.0);
  404.  
  405.   // if local ship, notify server that ship is alive
  406.   SwAliveMessage am;
  407.   server()->send(am);
  408. }
  409.  
  410. void            ShipObject::advance(float dt)
  411. {
  412.   if (active() != ObjectActive) return;
  413.   if (!exploding()) {
  414.     advanceEngine(dt);
  415.     advanceLaser(dt);
  416.   }
  417.   advanceShield(dt);
  418.   for (int i = 0; i < MAXMISSILES; i++)
  419.     advanceMissile(i, dt);
  420.   advanceOrientation(dt);
  421.   advancePosition(dt);
  422.   advanceExtra(dt);
  423.   advanceCamera();
  424.   if (exploding()) {
  425.     info.info.explodeTime -= dt / SHIP_EXPLODE;
  426.     if (info.info.explodeTime <= 0.0) {
  427.       info.info.explodeTime = 0.0;
  428.       active(ObjectInactive);
  429.     }
  430.   }
  431.   else {
  432.     // check if I've moved off any flags I can't grab
  433.     for (i = 0; i < NUMTEAMS; i++)
  434.       if (cantGrabFlag[i] && getTeam(Team(i)).state == FlagReady) {
  435.     TeamInfo& t = getTeam(Team(i));
  436.     float dx = t.position[0] - info.info.position[0],
  437.         dy = t.position[1] - info.info.position[1],
  438.         dz = t.position[2] - info.info.position[2];
  439.     if (dx*dx + dy*dy + dz*dz > (sbs+2.0*FLAGSIZE)*(sbs+2.0*FLAGSIZE))
  440.       cantGrabFlag[i] = FALSE;
  441.       }
  442.   }
  443. }
  444.  
  445. Active            ShipObject::active() const
  446. {
  447.   return info.info.active;
  448. }
  449.  
  450. void            ShipObject::active(Active a)
  451. {
  452.   if (info.info.active == a) return;
  453.   info.info.active = a;
  454.   switch (info.info.active) {
  455.     case ObjectInactive:                // not playing
  456.     activeSwitch->whichChild = SO_SWITCH_NONE;    // hide all
  457.     break;
  458.     case ObjectActive:                    // playing
  459.     activeSwitch->whichChild = SO_SWITCH_ALL;    // show all
  460.     break;
  461.     case ObjectPaused:                    // paused
  462.     activeSwitch->whichChild = SO_SWITCH_NONE;    // hide all
  463.     break;
  464.   }
  465. }
  466.  
  467. int            ShipObject::hittable() const
  468. {
  469.   return (info.info.active == ObjectActive && info.info.explodeTime == 0.0);
  470. }
  471.  
  472. float            ShipObject::shipBound() const
  473. {
  474.   return sbs;
  475. }
  476.  
  477. int            ShipObject::shipVisibility() const
  478. {
  479.   return shipVisible;
  480. }
  481.  
  482. void            ShipObject::shipVisibility(int v)
  483. {
  484.   if (shipVisible == v) return;
  485.   shipVisible = v;
  486.   if (v) {
  487.     shipSwitch->whichChild = SO_SWITCH_ALL;
  488.   }
  489.   else {
  490.     shipSwitch->whichChild = SO_SWITCH_NONE;
  491.   }
  492. }
  493.  
  494. ShipInfo&        ShipObject::shipInfo()
  495. {
  496.   return info;
  497. }
  498.  
  499. float            ShipObject::mass() const
  500. {
  501.   return DEFAULT_MASS;
  502. }
  503.  
  504. int            ShipObject::exploding() const
  505. {
  506.   return (info.info.explodeTime > 0.0);
  507. }
  508.  
  509. float            ShipObject::explodeTime() const
  510. {
  511.   return info.info.explodeTime;
  512. }
  513.  
  514. SbVec3f            ShipObject::position() const
  515. {
  516.   return SbVec3f(info.info.position);
  517. }
  518.  
  519. const SbRotation&    ShipObject::orientation() const
  520. {
  521.   return shipOrientation->rotation.getValue();
  522. }
  523.  
  524. const SbVec3f        ShipObject::direction() const
  525. {
  526.   return SbVec3f(info.info.direction);
  527. }
  528.  
  529. SbVec3f            ShipObject::velocity() const
  530. {
  531.   return SbVec3f(info.info.velocity);
  532. }
  533.  
  534. void            ShipObject::position(const SbVec3f& p)
  535. {
  536.   info.info.position[0] = p[0];
  537.   info.info.position[1] = p[1];
  538.   info.info.position[2] = p[2];
  539.   advance(0.0);
  540. }
  541. void            ShipObject::orientation(const SbRotation& r)
  542. {
  543.   shipOrientation->rotation.setValue(r);
  544.   advance(0.0);
  545. }
  546. void            ShipObject::velocity(const SbVec3f& v)
  547. {
  548.   info.info.velocity[0] = v[0];
  549.   info.info.velocity[1] = v[1];
  550.   info.info.velocity[2] = v[2];
  551.   advance(0.0);
  552. }
  553.  
  554. void            ShipObject::targetAngVel(const SbVec3f& avd)
  555. {
  556.   // limit angular velocity?
  557.   angularVelDirTarget = avd;
  558. }
  559.  
  560. float            ShipObject::angularAccel() const
  561. {
  562.   return DEFAULT_ANGACCEL;
  563. }
  564.  
  565. void            ShipObject::engineOutput(float o)
  566. {
  567.   if (exploding()) o = 0.0;            // no thrust when dead
  568.   else if (fuel == 0.0) {
  569.     if (o > SOLARTHRUST) o = SOLARTHRUST;    // no fuel limits max thrust
  570.     else if (o < -SOLARTHRUST) o = -SOLARTHRUST;
  571.   }
  572.   if (o < -1.0) o = -1.0;
  573.   else if (o > 1.0) o = 1.0;
  574.   engineOutputTarget = o;
  575. }
  576.  
  577. float            ShipObject::engineOutput() const
  578. {
  579.   return info.info.engineOutput;
  580. }
  581.  
  582. float            ShipObject::engineForce() const
  583. {
  584.   return info.info.engineOutput * engineMaxForce();
  585. }
  586.  
  587. float            ShipObject::engineMaxForce() const
  588. {
  589.   return DEFAULT_MAXFORCE;
  590. }
  591.  
  592. float            ShipObject::engineResponse() const
  593. {
  594.   return DEFAULT_RESPONSE;
  595. }
  596.  
  597. float            ShipObject::fuelLeft() const
  598. {
  599.   return fuel;
  600. }
  601.  
  602. void            ShipObject::fuelLeft(float f)
  603. {
  604.   if (f < 0.0) f = 0.0;
  605.   else if (f > 1.0) f = 1.0;
  606.   fuel = f;
  607. }
  608.  
  609. float            ShipObject::fuelRate() const
  610. {
  611.   return DEFAULT_FUELRATE;
  612. }
  613.  
  614. float            ShipObject::shield(Shield shieldNum) const
  615. {
  616.   return info.shieldStrength[int(shieldNum)];
  617. }
  618.  
  619. float            ShipObject::shieldMaximum() const
  620. {
  621.   return DEFAULT_MAXSHIELD;
  622. }
  623.  
  624. float            ShipObject::shieldRecovery() const
  625. {
  626.   return DEFAULT_SHIELDRECOVERY;
  627. }
  628.  
  629. void            ShipObject::shieldStrength(float de)
  630. {
  631.   for (int i = int(FrontShield); i <= int(BottomShield); i++) {
  632.     info.shieldStrength[i] += de;
  633.     if (info.shieldStrength[i] > 1.0)
  634.       info.shieldStrength[i] = 1.0;
  635.     else if (info.shieldStrength[i] < 0.0)
  636.      info.shieldStrength[i] = 0.0;
  637.   }
  638. }
  639.  
  640. inline float        ShipObject::dissipate(float d) const
  641. {
  642.   return d;
  643. }
  644.  
  645. int            ShipObject::fireMissile(ObjectInfo* target)
  646. {
  647.   if (!missileReady()) return FALSE;        // not ready to shoot
  648.  
  649.   // activate a missile and set up its initial stuff
  650.   ObjectInfo& m = info.missile[nextSilo];
  651.   m.active = ObjectActive;
  652.   m.mass = MISSILE_MASS;
  653.   m.explodeTime = 0.0;                // not exploding
  654.   m.engineOutput = 1.0;                // full power
  655.   SbVec3f p;
  656.   findWorldPosition(missileSource(nextSilo), p);
  657.   m.position[0] = p[0];
  658.   m.position[1] = p[1];
  659.   m.position[2] = p[2];
  660.   m.velocity[0] = info.info.velocity[0];
  661.   m.velocity[1] = info.info.velocity[1];
  662.   m.velocity[2] = info.info.velocity[2];
  663.   m.orientation[0] = info.info.orientation[0];
  664.   m.orientation[1] = info.info.orientation[1];
  665.   m.orientation[2] = info.info.orientation[2];
  666.   m.orientation[3] = info.info.orientation[3];
  667.   m.direction[0] = info.info.direction[0];
  668.   m.direction[1] = info.info.direction[1];
  669.   m.direction[2] = info.info.direction[2];
  670.   missileSwitch[nextSilo]->whichChild = SO_SWITCH_ALL;
  671.   missilePosition[nextSilo]->translation.setValue(m.position);
  672.   missileOrientation[nextSilo]->rotation.setValue(m.orientation);
  673.   missileTime[nextSilo] = MISSILE_TIME;        // time to live
  674.   missileTarget[nextSilo] = target;        // set target
  675.   missileVisible[nextSilo] = -1;
  676.   missileVisibility(nextSilo, FALSE);
  677.  
  678.   missilesLeft--;                // fired!
  679.   nextSilo = (nextSilo + 1) % missileSilos();    // prepare next silo in cycle
  680.   missileChanged();
  681.  
  682.   soundPlay(MissileSound);
  683.  
  684.   return TRUE;
  685. }
  686.  
  687. int            ShipObject::maxMissiles() const
  688. {
  689.   return DEFAULT_MAXMISSILES;
  690. }
  691.  
  692. int            ShipObject::numMissiles() const
  693. {
  694.   return missilesLeft;
  695. }
  696.  
  697. void            ShipObject::numMissiles(int m)
  698. {
  699.   if (m > maxMissiles()) m = maxMissiles();
  700.   missilesLeft = m;
  701. }
  702.  
  703. ObjectInfo&        ShipObject::missileInfo(int missileNum)
  704. {
  705.   return info.missile[missileNum];
  706. }
  707.  
  708. float            ShipObject::missileEnergy() const
  709. {
  710.   return DEFAULT_MISSILE_ENERGY;
  711. }
  712.  
  713. float            ShipObject::missileRadius() const
  714. {
  715.   return mbs * 5.0;
  716. }
  717.  
  718. float            ShipObject::missileBound() const
  719. {
  720.   return mbs;
  721. }
  722.  
  723. int            ShipObject::missileHittable(int num) const
  724. {
  725.   return (info.missile[num].active == ObjectActive &&
  726.                     info.missile[num].explodeTime == 0.0);
  727. }
  728.  
  729. int            ShipObject::missileVisibility(int num) const
  730. {
  731.   return missileVisible[num];
  732. }
  733.  
  734. void            ShipObject::missileVisibility(int num, int v)
  735. {
  736.   if (info.missile[num].active != ObjectActive && v) v = FALSE;
  737.   if (missileVisible[num] == v) return;
  738.   missileVisible[num] = v;
  739.   if (v) {
  740.     missileSwitch[num]->whichChild = SO_SWITCH_ALL;
  741.   }
  742.   else {
  743.     missileSwitch[num]->whichChild = SO_SWITCH_NONE;
  744.   }
  745. }
  746.  
  747. int            ShipObject::missileReady() const
  748. {
  749.   if (active() != ObjectActive || exploding())    // I'm dead
  750.     return FALSE;
  751.   if (missilesLeft <= 0) return FALSE;        // no missiles left
  752.   if (missileTime[nextSilo] != 0.0)        // reloading, can't fire
  753.     return FALSE;
  754.   return TRUE;                    // okay
  755. }
  756.  
  757. int            ShipObject::fireLaser()
  758. {
  759.   if (active() != ObjectActive || exploding())    // I'm dead
  760.     return FALSE;
  761.  
  762.   if (laserTemp >= 1.0 || info.firingLaser)    // too hot or already shooting
  763.     return FALSE;
  764.  
  765.   laserTime = 0.25;                // fire for a quarter second
  766.   info.firingLaser = TRUE;
  767.   laserSwitch->whichChild = SO_SWITCH_ALL;
  768.  
  769.   soundPlay(LaserSound);
  770.  
  771.   return TRUE;
  772. }
  773.  
  774. float            ShipObject::laserHeat() const
  775. {
  776.   return laserTemp;
  777. }
  778.  
  779. float            ShipObject::laserPower() const
  780. {
  781.   return DEFAULT_LASER_POWER;
  782. }
  783.  
  784. const SbVec3f&        ShipObject::turretPosition() const
  785. {
  786.   return laserLocation->translation.getValue();
  787. }
  788.  
  789. const SbVec3f&        ShipObject::trackDirection() const
  790. {
  791.   return trackingDirection;
  792. }
  793.  
  794. void            ShipObject::trackDirection(const SbVec3f& t)
  795. {
  796.   // limit target direction?
  797.   trackingDirTarget = t;
  798. }
  799.  
  800. SbVec3f            ShipObject::laserPosition() const
  801. {
  802.   SbVec3f p;
  803.   findWorldPosition(laserLocation->translation.getValue(), p);
  804.   return p;
  805. }
  806.  
  807. SbVec3f            ShipObject::laserDirection() const
  808. {
  809.   SbVec3f ld;
  810.   findWorldDirection(trackingDirection, ld);
  811.   return ld;
  812. }
  813.  
  814. void            ShipObject::advanceExtra(float)
  815. {
  816.   // do nothing
  817. }
  818.  
  819. void            ShipObject::read(const ShipInfo& si)
  820. {
  821.   // given a ship info from a broadcast packet, build other ship data
  822.   // first check to see if anything is newly exploding
  823.   if (info.info.active == ObjectActive && info.info.explodeTime == 0.0 &&
  824.                     si.info.explodeTime != 0.0)
  825.     soundPlay(ExplosionSound, explodeVolume((float *)si.info.position, 2.0));
  826.   for (int i = 0; i < MAXMISSILES; i++) {
  827.     if (info.missile[i].active != ObjectActive) continue;
  828.     if (info.missile[i].explodeTime == 0.0 && si.missile[i].explodeTime != 0.0)
  829.       soundPlay(ExplosionSound, explodeVolume((float *)si.missile[i].position));
  830.   }
  831.  
  832.   // set my ship info
  833.   info = si;
  834.  
  835.   // set ship active state
  836.   activeSwitch->whichChild = (info.info.active == ObjectActive) ?
  837.                         SO_SWITCH_ALL : SO_SWITCH_NONE;
  838.  
  839.   // set ship visibility
  840.   shipVisibility(info.info.active == ObjectActive &&
  841.                 info.info.explodeTime == 0.0);
  842.  
  843.   // set flag visibility
  844.   flag(info.flag);
  845.  
  846.   // set ship position
  847.   shipPosition->translation.setValue(info.info.position);
  848.  
  849.   // set ship rotation
  850.   rotation.setValue(info.info.orientation);
  851.   rotation.getValue(rotateMatrix);
  852.   shipOrientation->rotation.setValue(rotation);
  853.  
  854.   // set laser beam direction, length, and on/off switch
  855.   laserSwitch->whichChild = info.firingLaser ? SO_SWITCH_ALL : SO_SWITCH_NONE;
  856.   laserOrientation->rotation.setValue(SbRotation(straightAhead,
  857.                     SbVec3f(info.laserDirection)));
  858.   if (info.firingLaser) {
  859.     for (i = 1; i <= 9; i += 2)
  860.       laserBeamPoints[i][2] = -info.beamLength;
  861.     beamCoord->point.setValues(0, 10, laserBeamPoints);
  862.   }
  863.  
  864.   // set missile positions and orientations
  865.   for (i = 0; i < MAXMISSILES; i++) {
  866.     missileVisibility(i, info.missile[i].explodeTime == 0.0);
  867.     missilePosition[i]->translation.setValue(info.missile[i].position);
  868.     missileOrientation[i]->rotation.setValue(info.missile[i].orientation);
  869.   }
  870.  
  871.   // set shield hit stuff
  872.   for (i = 0; i < MAXHITS; i++) {
  873.     if (info.shieldHits[i][0] == 0.0)
  874.       hitSwitch[i]->whichChild = SO_SWITCH_NONE;    // turn off hit
  875.     else {
  876.       hitSwitch[i]->whichChild = SO_SWITCH_ALL;        // turn on hit
  877.  
  878.       // set position and scale
  879.       SbVec3f p(info.shieldHits[i] + 1);
  880.       hitTransform[i]->scaleOrientation.setValue(SbRotation(straightAhead, p));
  881.       hitTransform[i]->rotation.setValue(SbRotation(straightAhead, p));
  882.  
  883.       // set hit shading
  884.       float* t = hitMaterial[i]->transparency.startEditing();
  885.       for (int c = 0, m = -HITRES; m <= HITRES; m++)
  886.     for (int n = -HITRES; n <= HITRES; c++, n++) {
  887.       float d = (float)(m*m + n*n) / (2.0*HITRES*HITRES);
  888.       t[c] = 1.0 - (info.shieldHits[i][0] - dissipate(d));
  889.       if (t[c] > 1.0) t[c] = 1.0;
  890.       else if (t[c] < 0.0) t[c] = 0.0;
  891.     }
  892.       hitMaterial[i]->transparency.finishEditing();
  893.     }
  894.   }
  895. }
  896.  
  897. float            ShipObject::laserHitShip(const Ray& ray) const
  898. {
  899.   SbVec3f d, o(info.info.position[0] + sx - ray.o[0],
  900.         info.info.position[1] + sx - ray.o[1],
  901.         info.info.position[2] + sx - ray.o[2]);
  902.   rotateMatrix.multMatrixVec(ray.d, d);
  903.   rotateMatrix.multMatrixVec(o, o);
  904.   float x = o[0] / (SHIELDSIZE * sdx),
  905.     y = o[1] / (SHIELDSIZE * sdy),
  906.     z = o[2] / (SHIELDSIZE * sdz);
  907.   float dx = d[0] / (SHIELDSIZE * sdx),
  908.     dy = d[1] / (SHIELDSIZE * sdy),
  909.     dz = d[2] / (SHIELDSIZE * sdz);
  910.   float    dist, dd = 1.0 / sqrt(dx*dx + dy*dy + dz*dz);
  911.  
  912.   float l2 = x * x + y * y + z * z;        // dist from sphere center
  913.   float tca = (x * dx + y * dy + z * dz) * dd;    // closest approach parameter
  914.   float thc;
  915.   if (l2 <= 1.0) {                // ray origin inside shields
  916.     dist = tca + sqrt(1.0 - l2 + tca*tca);    //  good intersection
  917.   }
  918.   else {                    // starts outside
  919.     if (tca < 0.0) return -1.0;            //  center behind ray -- miss
  920.     thc = 1.0 - l2 + tca*tca;
  921.     if (thc < 0.0) return -1.0;            //  to side of sphere -- miss
  922.     thc = sqrt(thc);
  923.     dist = tca - thc;                //  good intersection
  924.   }
  925.   return dist * dd;
  926. }
  927.  
  928. float            ShipObject::laserHitMissile(const Ray& r, int n) const
  929. {
  930.   if (!missileHittable(n)) return -1.0;        // no such missile
  931.  
  932.   // intersect against ellipsoid around missile
  933.   SbRotation rot(info.missile[n].orientation);    // get rotation
  934.   SbMatrix m;
  935.   rot.getValue(m);
  936.   SbVec3f d, o(info.missile[n].position[0] - r.o[0],
  937.         info.missile[n].position[1] - r.o[1],
  938.         info.missile[n].position[2] - r.o[2]);
  939.   m.multMatrixVec(r.d, d);
  940.   m.multMatrixVec(o, o);
  941.   float x = o[0] / (MISSILE_SIZE * mdx),
  942.     y = o[1] / (MISSILE_SIZE * mdy),
  943.     z = o[2] / (MISSILE_SIZE * mdz);
  944.   float dx = d[0] / (MISSILE_SIZE * mdx),
  945.     dy = d[1] / (MISSILE_SIZE * mdy),
  946.     dz = d[2] / (MISSILE_SIZE * mdz);
  947.   float    dist, dd = 1.0 / sqrt(dx*dx + dy*dy + dz*dz);
  948.  
  949.   float l2 = x * x + y * y + z * z;        // dist from sphere center
  950.   float tca = (x * dx + y * dy + z * dz) * dd;    // closest approach parameter
  951.   float thc;
  952.   if (l2 <= 1.0) {                // ray origin inside shields
  953.     dist = tca + sqrt(1.0 - l2 + tca*tca);    //  good intersection
  954.   }
  955.   else {                    // starts outside
  956.     if (tca < 0.0) return -1.0;            //  center behind ray -- miss
  957.     thc = 1.0 - l2 + tca*tca;
  958.     if (thc < 0.0) return -1.0;            //  to side of sphere -- miss
  959.     thc = sqrt(thc);
  960.     dist = tca - thc;                //  good intersection
  961.   }
  962.   return dist * dd;
  963. }
  964.  
  965. float            ShipObject::laserHitAsteroid(const Ray& r, int a) const
  966. {
  967.   float* ap = asteroidPosition(a);
  968.   float radius = asteroidRadius(a);
  969.   float x = ap[0] - r.o[0],
  970.     y = ap[1] - r.o[1],
  971.     z = ap[2] - r.o[2];
  972.   float dx = r.d[0],
  973.     dy = r.d[1],
  974.     dz = r.d[2];
  975.   float    d, dd = sqrt(dx * dx + dy * dy + dz * dz);
  976.   float r2 = radius * radius;
  977.  
  978.   float l2 = x * x + y * y + z * z;        // dist from sphere center
  979.   float tca = (x * dx + y * dy + z * dz) / dd;    // closest approach parameter
  980.   if (l2 <= r2) {                // ray origin inside shields
  981.     d = tca + sqrt(r2 - l2 + tca*tca);        //  good intersection
  982.   }
  983.   else {                    // starts outside
  984.     if (tca < 0.0) return -1.0;            //  center behind ray -- miss
  985.     float thc = r2 - l2 + tca*tca;
  986.     if (thc < 0.0) return -1.0;            //  to side of sphere -- miss
  987.     d = tca - sqrt(thc);            //  good intersection
  988.   }
  989.   return d / dd;
  990. }
  991.  
  992. float            ShipObject::missileHitShip(const Ray& r, float radius)
  993.                                     const
  994. {
  995.   // find ray in ship space
  996.   float x = info.info.position[0] + sx - r.o[0],
  997.     y = info.info.position[1] + sy - r.o[1],
  998.     z = info.info.position[2] + sz - r.o[2];
  999.   float dx = r.d[0] - info.info.velocity[0],
  1000.     dy = r.d[1] - info.info.velocity[1],
  1001.     dz = r.d[2] - info.info.velocity[2];
  1002.   float    d, dd = sqrt(dx * dx + dy * dy + dz * dz);
  1003.   if (dd < 1e-4) return -1.0;            // ray not moving
  1004.   radius += shipBound();            // increase radius by ship size
  1005.   float r2 = radius * radius;
  1006.  
  1007.   float l2 = x * x + y * y + z * z;        // dist from sphere center
  1008.   float tca = (x * dx + y * dy + z * dz) / dd;    // closest approach parameter
  1009.   if (l2 <= r2) {                // ray origin inside shields
  1010.     d = tca + sqrt(r2 - l2 + tca*tca);        //  good intersection
  1011.   }
  1012.   else {                    // starts outside
  1013.     if (tca < 0.0) return -1.0;            //  center behind ray -- miss
  1014.     float thc = r2 - l2 + tca*tca;
  1015.     if (thc < 0.0) return -1.0;            //  to side of sphere -- miss
  1016.     d = tca - sqrt(thc);            //  good intersection
  1017.   }
  1018.   return d / dd;
  1019. }
  1020.  
  1021. float            ShipObject::missileHitMissile(const Ray& r,
  1022.                         float radius, int n) const
  1023. {
  1024.   if (!missileHittable(n)) return -1.0;        // no such missile
  1025.  
  1026.   float x = info.missile[n].position[0] - r.o[0],
  1027.     y = info.missile[n].position[1] - r.o[1],
  1028.     z = info.missile[n].position[2] - r.o[2];
  1029.   float dx = r.d[0] - info.missile[n].velocity[0],
  1030.     dy = r.d[1] - info.missile[n].velocity[1],
  1031.     dz = r.d[2] - info.missile[n].velocity[2];
  1032.   float    d, dd = sqrt(dx * dx + dy * dy + dz * dz);
  1033.   if (dd < 1e-4) return -1.0;            // ray not moving
  1034.   float r2 = radius * radius;
  1035.  
  1036.   float l2 = x * x + y * y + z * z;        // dist from sphere center
  1037.   float tca = (x * dx + y * dy + z * dz) / dd;    // closest approach parameter
  1038.   if (l2 <= r2) {                // ray origin inside shields
  1039.     d = tca + sqrt(r2 - l2 + tca*tca);        //  good intersection
  1040.   }
  1041.   else {                    // starts outside
  1042.     if (tca < 0.0) return -1.0;            //  center behind ray -- miss
  1043.     float thc = r2 - l2 + tca*tca;
  1044.     if (thc < 0.0) return -1.0;            //  to side of sphere -- miss
  1045.     d = tca - sqrt(thc);            //  good intersection
  1046.   }
  1047.   return d / dd;
  1048. }
  1049.  
  1050. float            ShipObject::missileHitAsteroid(const Ray& r,
  1051.                         float radius, int a) const
  1052. {
  1053.   float* ap = asteroidPosition(a);
  1054.   radius += asteroidRadius(a);
  1055.   float x = ap[0] - r.o[0],
  1056.     y = ap[1] - r.o[1],
  1057.     z = ap[2] - r.o[2];
  1058.   float dx = r.d[0],
  1059.     dy = r.d[1],
  1060.     dz = r.d[2];
  1061.   float    d, dd = sqrt(dx * dx + dy * dy + dz * dz);
  1062.   if (dd < 1e-4) return -1.0;            // ray not moving
  1063.   float r2 = radius * radius;
  1064.  
  1065.   float l2 = x * x + y * y + z * z;        // dist from sphere center
  1066.   float tca = (x * dx + y * dy + z * dz) / dd;    // closest approach parameter
  1067.   if (l2 <= r2) {                // ray origin inside shields
  1068.     d = tca + sqrt(r2 - l2 + tca*tca);        //  good intersection
  1069.   }
  1070.   else {                    // starts outside
  1071.     if (tca < 0.0) return -1.0;            //  center behind ray -- miss
  1072.     float thc = r2 - l2 + tca*tca;
  1073.     if (thc < 0.0) return -1.0;            //  to side of sphere -- miss
  1074.     d = tca - sqrt(thc);            //  good intersection
  1075.   }
  1076.   return d / dd;
  1077. }
  1078.  
  1079. void            ShipObject::explodeShip(InAddr killer)
  1080. {
  1081.   // Note: only called for local object
  1082.   // I'm blowing up now
  1083.   info.info.explodeTime = 1.0;
  1084.  
  1085.   // drop whatever flag I had
  1086.   dropFlag();
  1087.  
  1088.   // send message that I was killed
  1089.   if (!NetId(killer).isAny()) {
  1090.     SwKilledMessage m;
  1091.     m.killer = killer;                // who destroyed me
  1092.     m.victim = id();                // who got destroyed (me!)
  1093.     server()->send(m);
  1094.   }
  1095.  
  1096.   // can't target when I'm dead
  1097.   noTarget();
  1098.  
  1099.   // no thrust when blowing up
  1100.   info.info.engineOutput = engineOutputTarget = 0.0;
  1101.   fuel = 0.0;
  1102.   thrustChanged();
  1103.   fuelChanged();
  1104.  
  1105.   // shut off laser
  1106.   info.firingLaser = FALSE;
  1107.   laserSwitch->whichChild = SO_SWITCH_NONE;
  1108.   laserTemp = 0.0;
  1109.   laserTime = 0.0;
  1110.   laserChanged();
  1111.  
  1112.   // take away remaining missiles
  1113.   missilesLeft = 0;
  1114.   missileChanged();
  1115.  
  1116.   // shields all gone (and not hits visible)
  1117.   for (int i = int(FrontShield); i <= int(BottomShield); i++)
  1118.     info.shieldStrength[i] = 0.0;
  1119.   shieldsChanged();
  1120.   for (i = 0; i < MAXHITS; i++) {
  1121.     info.shieldHits[i][0] = 0.0;
  1122.     hitSwitch[i]->whichChild = SO_SWITCH_NONE;
  1123.   }
  1124.  
  1125.   soundPlay(ExplosionSound);
  1126. }
  1127.  
  1128. void            ShipObject::explodeMissile(const SwHitMessage& m)
  1129. {
  1130.   if (!self(m.victim)) {            // remote ship
  1131.     server()->send(m);                //  send message
  1132.     return;
  1133.   }
  1134.  
  1135.   if (!missileHittable(m.missileNum)) return;    // not active or exploding
  1136.  
  1137.   // now exploding
  1138.   info.missile[m.missileNum].explodeTime = 1.0;
  1139.   if (missileTime[m.missileNum] < MISSILE_EXPLODE)
  1140.     missileTime[m.missileNum] = MISSILE_EXPLODE;
  1141.  
  1142.   // missile geometry invisible (explosion is)
  1143.   missileSwitch[m.missileNum]->whichChild = SO_SWITCH_NONE;
  1144.  
  1145.   // modify position and velocity due to impact
  1146.   info.missile[m.missileNum].position[0] = m.position[0];
  1147.   info.missile[m.missileNum].position[1] = m.position[1];
  1148.   info.missile[m.missileNum].position[2] = m.position[2];
  1149.   info.missile[m.missileNum].velocity[0] += m.velocityChange[0];
  1150.   info.missile[m.missileNum].velocity[1] += m.velocityChange[1];
  1151.   info.missile[m.missileNum].velocity[2] += m.velocityChange[2];
  1152.  
  1153.   soundPlay(ExplosionSound, explodeVolume(info.missile[m.missileNum].position));
  1154. }
  1155.  
  1156. void            ShipObject::dropFlag()
  1157. {
  1158.   if (flag() == NoTeam) return;
  1159.  
  1160.   SwDropFlagMessage m;
  1161.   m.team = flag();
  1162.   SbVec3f p;
  1163.   rotation.getValue(rotateMatrix);
  1164.   findWorldPosition(flagPosition, p);
  1165.   m.position[0] = p[0];
  1166.   m.position[1] = p[1];
  1167.   m.position[2] = p[2];
  1168.   server()->send(m);
  1169.  
  1170.   if (self(this))                // can't grab flag til I move
  1171.     cantGrabFlag[int(flag())] = TRUE;
  1172.  
  1173.   flag(NoTeam);
  1174.   flagChanged();
  1175. }
  1176.  
  1177. void            ShipObject::grabFlag(Team t)
  1178. {
  1179.   if (flag() != NoTeam || cantGrabFlag[int(t)]) return;
  1180.  
  1181.   SwGrabFlagMessage m;
  1182.   m.team = t;
  1183.   server()->send(m);
  1184. }
  1185.  
  1186. void            ShipObject::hitShield(const SwHitMessage& hm)
  1187. {
  1188.   if (!self(hm.victim)) {            // remote ship
  1189.     server()->send(hm);                //  send message
  1190.     return;
  1191.   }
  1192.  
  1193.   if (!hittable()) return;            // not active or exploding
  1194.  
  1195.   // modify ship's velocity due to impact
  1196.   // what about angular velocity?
  1197.   info.info.velocity[0] += hm.velocityChange[0];
  1198.   info.info.velocity[1] += hm.velocityChange[1];
  1199.   info.info.velocity[2] += hm.velocityChange[2];
  1200.  
  1201.   // find available hit slot
  1202.   for (int b = 0, j = 1; j < MAXHITS; j++)
  1203.     if (info.shieldHits[j][0] < info.shieldHits[b][0])
  1204.       b = j;
  1205.  
  1206.   // turn on hit geometry
  1207.   SbVec3f i(hm.position);            // local direction of hit
  1208.   hitSwitch[b]->whichChild = SO_SWITCH_ALL;
  1209.   hitTransform[b]->scaleOrientation.setValue(SbRotation(straightAhead, i));
  1210.   hitTransform[b]->rotation.setValue(SbRotation(straightAhead, i));
  1211.   info.shieldHits[b][0] = 10.0 * hm.energy / DEFAULT_LASER_POWER;
  1212.   if (info.shieldHits[b][0] > 1.25) info.shieldHits[b][0] = 1.25;
  1213.   info.shieldHits[b][1] = i[0];
  1214.   info.shieldHits[b][2] = i[1];
  1215.   info.shieldHits[b][3] = i[2];
  1216.  
  1217.   // set hit shading
  1218.   float* t = hitMaterial[b]->transparency.startEditing();
  1219.   for (int c = 0, m = -HITRES; m <= HITRES; m++)
  1220.     for (int n = -HITRES; n <= HITRES; c++, n++) {
  1221.       float d = (float)(m*m + n*n) / (2.0*HITRES*HITRES);
  1222.       t[c] = 1.0 - (info.shieldHits[b][0] - dissipate(d));
  1223.       if (t[c] > 1.0) t[c] = 1.0;
  1224.       else if (t[c] < 0.0) t[c] = 0.0;
  1225.     }
  1226.   hitMaterial[b]->transparency.finishEditing();
  1227.  
  1228.   // remove strength from shield
  1229.   int s = int(shieldNumber(i));            // which shield was hit
  1230.   if (info.shieldStrength[s] > 0.0) {        // shield still up
  1231.     if ((info.shieldStrength[s] -= hm.energy / shieldMaximum()) < 0.0)
  1232.       if (info.shieldStrength[s] > -0.1)
  1233.     info.shieldStrength[s] = 0.0;        // shield has collapsed
  1234.       else
  1235.     explodeShip(hm.hitter);            // too much extra, blow up
  1236.   }
  1237.   else                        // shield was collapsed
  1238.     explodeShip(hm.hitter);            // blow up ship
  1239. }
  1240.  
  1241. ShipObject::Shield            ShipObject::shieldNumber(const SbVec3f& i) const
  1242. {
  1243.   // find which shield
  1244.   //   Note: i must be on the unit sphere
  1245.   if (i[1] > M_SQRT1_2) return TopShield;
  1246.   if (i[1] < -M_SQRT1_2) return BottomShield;
  1247.   if (fabs(i[0]) > fabs(i[2])) {
  1248.     if (i[0] < 0.0) return LeftShield;
  1249.     else return RightShield;
  1250.   }
  1251.   else {
  1252.     if (i[2] < 0.0) return FrontShield;
  1253.     else return RearShield;
  1254.   }
  1255. }
  1256.  
  1257. void            ShipObject::advanceCamera()
  1258. {
  1259.   if (self(this)) {
  1260.     SbVec3f cp;
  1261.     findWorldDirection(cockpit, cp);
  1262.     ::position->translation.setValue(-info.info.position[0] - cp[0],
  1263.                     -info.info.position[1] - cp[1],
  1264.                     -info.info.position[2] - cp[2]);
  1265.     camera->orientation.setValue(orientation());
  1266.     orientation().getValue(rotateMatrix);
  1267.   }
  1268. }
  1269.  
  1270. void            ShipObject::advancePosition(float dt)
  1271. {
  1272.   // advance ship velocity in world space
  1273.   float dv = dt * engineForce() / mass();    // change in velocity
  1274.   info.info.velocity[0] += dv * info.info.direction[0];
  1275.   info.info.velocity[1] += dv * info.info.direction[1];
  1276.   info.info.velocity[2] += dv * info.info.direction[2];
  1277.  
  1278.   // advance ship position in world space
  1279.   info.info.position[0] += dt * info.info.velocity[0];
  1280.   info.info.position[1] += dt * info.info.velocity[1];
  1281.   info.info.position[2] += dt * info.info.velocity[2];
  1282.   shipPosition->translation.setValue(info.info.position);
  1283. }
  1284.  
  1285. void            ShipObject::advanceOrientation(float dt)
  1286. {
  1287.   // advance angular velocity (toward target angular velocity)
  1288.   advanceTurn(angularVelDirTarget, angularVelDir, angularAccel(), dt);
  1289.  
  1290.   // advance ship orientation
  1291.   SbVec3f a = straightAhead.cross(angularVelDir);
  1292.   float ang = asin(a.length());
  1293.   SbRotation angVel(a, ang * ANGVELMULT * dt);
  1294.   shipOrientation->rotation.setValue(angVel * orientation());
  1295.   orientation().getValue(info.info.orientation[0], info.info.orientation[1],
  1296.             info.info.orientation[2], info.info.orientation[3]);
  1297.  
  1298.   // compute world space forward vector
  1299.   SbVec3f f;
  1300.   findWorldDirection(straightAhead, f);
  1301.   f.getValue(info.info.direction[0], info.info.direction[1],
  1302.                         info.info.direction[2]);
  1303. }
  1304.  
  1305. void            ShipObject::advanceEngine(float dt)
  1306. {
  1307.   // use up fuel if thrusting
  1308.   if (fuel > 0.0 && fabs(engineOutput()) > 0.0) {
  1309.     fuel -= dt * fuelRate() * fabs(engineOutput());
  1310.     if (fuel <= 0.0) fuel = 0.0;        // ran out of fuel
  1311.     fuelChanged();
  1312.   }
  1313.  
  1314.   // limit thrust if out of fuel
  1315.   if (fuel == 0.0) {
  1316.     if (engineOutputTarget > SOLARTHRUST)
  1317.       engineOutputTarget = SOLARTHRUST;
  1318.     else if (engineOutputTarget < -SOLARTHRUST)
  1319.       engineOutputTarget = -SOLARTHRUST;
  1320.     if (info.info.engineOutput > SOLARTHRUST)
  1321.       info.info.engineOutput = SOLARTHRUST;
  1322.     else if (info.info.engineOutput < -SOLARTHRUST)
  1323.       info.info.engineOutput = -SOLARTHRUST;
  1324.   }
  1325.  
  1326.   // get thrust level toward target
  1327.   if (engineOutputTarget != info.info.engineOutput) {
  1328.     if (fabs(engineOutputTarget - info.info.engineOutput) <= engineResponse())
  1329.       info.info.engineOutput = engineOutputTarget;
  1330.     else if (engineOutputTarget > info.info.engineOutput)
  1331.       info.info.engineOutput += engineResponse();
  1332.     else
  1333.       info.info.engineOutput -= engineResponse();
  1334.     thrustChanged();
  1335.     newEngineOutput();
  1336.   }
  1337. }
  1338.  
  1339. void            ShipObject::advanceShield(float dt)
  1340. {
  1341.   // recover shield strength
  1342.   int shieldChange = FALSE;
  1343.   float sr = dt * shieldRecovery() / shieldMaximum();
  1344.   if (info.info.explodeTime != 0.0) sr = 0.0;    // no recovery if dead
  1345.   for (int i = int(FrontShield); i <= int(BottomShield); i++)
  1346.     if (info.shieldStrength[i] < 1.0 &&        // if not full strength
  1347.     info.shieldStrength[i] != 0.0) {    // and not collapsed
  1348.       if (info.shieldStrength[i] + sr >= 1.0)
  1349.     info.shieldStrength[i] = 1.0;        // fully recovered
  1350.       else
  1351.     info.shieldStrength[i] += sr;        // recover some more
  1352.       shieldChange = TRUE;
  1353.     }
  1354.   if (shieldChange) shieldsChanged();
  1355.  
  1356.   // dissipate hits
  1357.   float de = dt * DISSIPATE_RATE;
  1358.   for (i = 0; i < MAXHITS; i++) {
  1359.     if (info.shieldHits[i][0] > 0.0) {
  1360.       info.shieldHits[i][0] -= de;        // dissipate impact
  1361.       if (info.shieldHits[i][0] <= 0.0) {    // dissipated -- shut off hit
  1362.     info.shieldHits[i][0] = 0.0;
  1363.     hitSwitch[i]->whichChild = SO_SWITCH_NONE;
  1364.       }
  1365.       else {                    // still dissipating
  1366.     float* t = hitMaterial[i]->transparency.startEditing();
  1367.     for (int c = 0, j = -HITRES; j <= HITRES; j++)
  1368.       for (int k = -HITRES; k <= HITRES; c++, k++) {
  1369.         float d = (float)(j*j + k*k) / (2.0*HITRES*HITRES);
  1370.         t[c] = 1.0 - (info.shieldHits[i][0] - dissipate(d));
  1371.         if (t[c] > 1.0) t[c] = 1.0;
  1372.         else if (t[c] < 0.0) t[c] = 0.0;
  1373.       }
  1374.     hitMaterial[i]->transparency.finishEditing();
  1375.       }
  1376.     }
  1377.   }
  1378. }
  1379.  
  1380. void            ShipObject::advanceMissile(int num, float dt)
  1381. {
  1382.   if (info.missile[num].active==ObjectActive) {    // missile exists
  1383.     ObjectInfo& m = info.missile[num];
  1384.     float t = (missileTime[num] > dt) ? dt : missileTime[num];
  1385.  
  1386.     // if exploding, advance time and return
  1387.     if (m.explodeTime > 0.0) {
  1388.       m.explodeTime -= dt / MISSILE_EXPLODE;
  1389.       if (m.explodeTime <= 0.0) {        // done exploding
  1390.     m.active = ObjectInactive;
  1391.     missileVisibility(num, FALSE);
  1392.     m.explodeTime = 0.0;
  1393.       }
  1394.     }
  1395.     else {
  1396.       if (missileTarget[num] && (missileTarget[num]->active != ObjectActive ||
  1397.                 missileTarget[num]->explodeTime != 0.0))
  1398.     missileTarget[num] = NULL;        // target disappeared
  1399.       if (missileTarget[num]) {            // target exists; aim at it
  1400.     if (missileTime[num] < MISSILE_TIME - GLIDE_TIME) {
  1401.       // make rough prediction of target's relative position at impact time
  1402.       SbVec3f p(missileTarget[num]->position);
  1403.       p[0] -= m.position[0];
  1404.       p[1] -= m.position[1];
  1405.       p[2] -= m.position[2];        // rel. pos. w/o velocity
  1406.       SbVec3f v(m.velocity);
  1407.       float vm = v.length();        // speed
  1408.       if (vm > 1.0) {            // only if missile moving
  1409.         float tti = p.length() / vm;    // approximate time to impact
  1410.         p[0] = missileTarget[num]->position[0] - m.position[0] +
  1411.         tti * (missileTarget[num]->velocity[0] - 0.75*m.velocity[0]);
  1412.         p[1] = missileTarget[num]->position[1] - m.position[1] +
  1413.         tti * (missileTarget[num]->velocity[1] - 0.75*m.velocity[1]);
  1414.         p[2] = missileTarget[num]->position[2] - m.position[2] +
  1415.         tti * (missileTarget[num]->velocity[2] - 0.75*m.velocity[2]);
  1416.       }
  1417.  
  1418.       // turn toward predicted position
  1419.       p.normalize();            // direction to target
  1420.       SbVec3f d(m.direction);        // direction missile pointing
  1421.       advanceTurn(p, d, MISSILE_ANG, dt);    // turn toward target
  1422.       SbRotation r(straightAhead, d);    // find rotation
  1423.       r.getValue(m.orientation[0], m.orientation[1],
  1424.             m.orientation[2], m.orientation[3]);
  1425.       missileOrientation[num]->rotation.setValue(r);
  1426.       d.getValue(m.direction[0], m.direction[1], m.direction[2]);
  1427.     }
  1428.       }
  1429.       else {                    // no target
  1430.     // got me.  I guess we go in a straight line.
  1431.       }
  1432.  
  1433.       // advance missile velocity in world space
  1434.       float dv = t * m.engineOutput * MISSILE_ACCEL;
  1435.       m.velocity[0] += dv * m.direction[0];
  1436.       m.velocity[1] += dv * m.direction[1];
  1437.       m.velocity[2] += dv * m.direction[2];
  1438.  
  1439.       {
  1440.     Ray r;
  1441.     r.o.setValue(m.position);
  1442.     r.d.setValue(m.velocity);
  1443.  
  1444.     float hitTime, bestTime = t;
  1445.     int bestShip = -1, bestMissile = -1, bestAsteroid = -1;
  1446.     for (int i = 0; i < MAXPLAYERS; i++) {
  1447.       ShipObject* s = getPlayer(i);
  1448.       if (!s || s->active() != ObjectActive ||
  1449.         (self(s) && missileTime[num] >= MISSILE_TIME - GLIDE_TIME))
  1450.         continue;
  1451.       ShipInfo& si = s->shipInfo();
  1452.       hitTime = s->missileHitShip(r, missileRadius());
  1453.       if (hitTime >= 0.0 && hitTime <= bestTime) {
  1454.         bestTime = hitTime;
  1455.         bestShip = i;
  1456.         bestMissile = -1;
  1457.       }
  1458.  
  1459.       if (!self(s)) {
  1460.         for (int j = 0; j < MAXMISSILES; j++) {
  1461.           hitTime = s->missileHitMissile(r, missileRadius(), j);
  1462.           if (hitTime >= 0.0 && hitTime <= bestTime) {
  1463.         bestTime = hitTime;
  1464.         bestShip = i;
  1465.         bestMissile = j;
  1466.           }
  1467.         }
  1468.       }
  1469.     }
  1470.     for (i = 0; i < numberAsteroids(); i++) {
  1471.       hitTime = missileHitAsteroid(r, missileRadius(), i);
  1472.       if (hitTime >= 0.0 && hitTime <= bestTime) {
  1473.         bestTime = hitTime;
  1474.         bestAsteroid = i;
  1475.       }
  1476.     }
  1477.  
  1478.     if (bestAsteroid != -1) {
  1479.       SwHitMessage hm;
  1480.       hm.hitter = id();
  1481.       hm.victim = id();
  1482.       hm.missileNum = num;
  1483.       hm.energy = missileEnergy();
  1484.       hm.position[0] = r.o[0] + bestTime * r.d[0];
  1485.       hm.position[1] = r.o[1] + bestTime * r.d[1];
  1486.       hm.position[2] = r.o[2] + bestTime * r.d[2];
  1487.       hm.velocityChange[0] = -m.velocity[0];
  1488.       hm.velocityChange[1] = -m.velocity[1];
  1489.       hm.velocityChange[2] = -m.velocity[2];
  1490.       explodeMissile(hm);            // explode my missile
  1491.     }
  1492.     else if (bestShip != -1) {
  1493.       SwHitMessage hm;
  1494.       hm.hitter = id();
  1495.       hm.victim = id();
  1496.       hm.missileNum = num;
  1497.       hm.energy = missileEnergy();
  1498.       hm.position[0] = r.o[0] + bestTime * r.d[0];
  1499.       hm.position[1] = r.o[1] + bestTime * r.d[1];
  1500.       hm.position[2] = r.o[2] + bestTime * r.d[2];
  1501.  
  1502.       ObjectInfo* hitObject;
  1503.       ShipObject* hitShip = getPlayer(bestShip);
  1504.       ShipInfo& hitInfo = hitShip->shipInfo();
  1505.       if (bestMissile == -1) {
  1506.         hitObject = &(hitInfo.info);
  1507.  
  1508.         // FIXME -- compute correct momentum transfer
  1509.         // make missile explosion travel with hit ship
  1510.         hm.velocityChange[0] = hitObject->velocity[0]-m.velocity[0];
  1511.         hm.velocityChange[1] = hitObject->velocity[1]-m.velocity[1];
  1512.         hm.velocityChange[2] = hitObject->velocity[2]-m.velocity[2];
  1513.         explodeMissile(hm);            // explode my missile
  1514.  
  1515.         // setup message for hit ship
  1516.         hm.victim = hitShip->id();
  1517.         hm.missileNum = -1;
  1518.         hm.velocityChange[0] = 0.0;
  1519.         hm.velocityChange[1] = 0.0;
  1520.         hm.velocityChange[2] = 0.0;
  1521.  
  1522.         // find local direction of impact at time bestTime
  1523.         SbVec3f hitPos(hm.position[0], hm.position[1], hm.position[2]);
  1524.         // ship would have moved when impact occurred
  1525.         hitPos -= SbVec3f(bestTime * hitObject->velocity[0],
  1526.                 bestTime * hitObject->velocity[1],
  1527.                 bestTime * hitObject->velocity[2]);
  1528.         hitShip->findLocalPosition(hitPos, hitPos);
  1529.         hm.position[0] = hitPos[0] / (SHIELDSIZE * sdx);
  1530.         hm.position[1] = hitPos[1] / (SHIELDSIZE * sdy);
  1531.         hm.position[2] = hitPos[2] / (SHIELDSIZE * sdz);
  1532.  
  1533.         // send hit message to hit ship
  1534.         hitShip->hitShield(hm);
  1535.       }
  1536.       else {
  1537.         hitObject = &(hitInfo.missile[bestMissile]);
  1538.  
  1539.         // FIXME -- compute correct momentum transfer
  1540.         // make missile explosion travel with hit ship
  1541.         hm.velocityChange[0] = hitObject->velocity[0]-m.velocity[0];
  1542.         hm.velocityChange[1] = hitObject->velocity[1]-m.velocity[1];
  1543.         hm.velocityChange[2] = hitObject->velocity[2]-m.velocity[2];
  1544.         explodeMissile(hm);            // explode my missile
  1545.  
  1546.         // setup message for hit missile's ship
  1547.         hm.victim = hitShip->id();
  1548.         hm.missileNum = bestMissile;
  1549.         hm.velocityChange[0] = m.velocity[0]-hitObject->velocity[0];
  1550.         hm.velocityChange[1] = m.velocity[1]-hitObject->velocity[1];
  1551.         hm.velocityChange[2] = m.velocity[2]-hitObject->velocity[2];
  1552.  
  1553.         // send hit message to hit missile's ship
  1554.         hitShip->explodeMissile(hm);
  1555.       }
  1556.     }
  1557.       }
  1558.  
  1559.       if (missileTime[num] - dt <= MISSILE_EXPLODE && m.explodeTime == 0.0) {
  1560.     SwHitMessage hm;
  1561.     hm.hitter = id();
  1562.     hm.victim = id();
  1563.     hm.missileNum = num;
  1564.     hm.energy = missileEnergy();
  1565.     hm.position[0] = m.position[0];
  1566.     hm.position[1] = m.position[1];
  1567.     hm.position[2] = m.position[2];
  1568.     hm.velocityChange[0] = 0.0;
  1569.     hm.velocityChange[1] = 0.0;
  1570.     hm.velocityChange[2] = 0.0;
  1571.     explodeMissile(hm);
  1572.       }
  1573.     }
  1574.  
  1575.     // advance missile position in world space
  1576.     m.position[0] += t * m.velocity[0];
  1577.     m.position[1] += t * m.velocity[1];
  1578.     m.position[2] += t * m.velocity[2];
  1579.     missilePosition[num]->translation.setValue(m.position);
  1580.   }
  1581.  
  1582.   // decrement reload time if reloading
  1583.   if (missileTime[num] > 0.0)
  1584.     if ((missileTime[num] -= dt) <= 0.0) {
  1585.       missileTime[num] = 0.0;            // finished reloading
  1586.       missileChanged();
  1587.     }
  1588. }
  1589.  
  1590. void            ShipObject::advanceLaser(float dt)
  1591. {
  1592.   int laserTempChanged = (laserTemp > 0.0 || info.firingLaser);
  1593.  
  1594.   // rotate laser (toward target direction)
  1595.   advanceTurn(trackingDirTarget, trackingDirection, MAXTRACKACC, dt);
  1596.   laserOrientation->rotation.setValue(SbRotation(straightAhead,
  1597.                             trackingDirection));
  1598.   SbVec3f ld = laserDirection();
  1599.   findWorldDirection(trackingDirection, ld);
  1600.   info.laserDirection[0] = ld[0];
  1601.   info.laserDirection[1] = ld[1];
  1602.   info.laserDirection[2] = ld[2];
  1603.  
  1604.   // cool off laser
  1605.   if (laserTemp > 0.0) {
  1606.     laserTemp -= 0.1 * (dt / 1.0);
  1607.     if (laserTemp < 0.0) laserTemp = 0.0;
  1608.   }
  1609.  
  1610.   if (info.firingLaser) {            // if we're firing check hits
  1611.     float t = (laserTime >= dt) ? dt : laserTime;
  1612.     Ray r;
  1613.     r.o = laserPosition();
  1614.     r.d = ld;
  1615.  
  1616.     float hitDist, bestDist = LASERRANGE;
  1617.     int bestShip = -1, bestMissile = -1, bestAsteroid = -1;
  1618.  
  1619.     for (int i = 0; i < MAXPLAYERS; i++) {
  1620.       ShipObject* s = getPlayer(i);
  1621.       if (!s || s->active() != ObjectActive) continue;
  1622.       ShipInfo& si = s->shipInfo();
  1623.       if (!self(s)) {
  1624.     hitDist = s->laserHitShip(r);
  1625.     if (hitDist >= 0.0 && hitDist < bestDist) {
  1626.       bestDist = hitDist;
  1627.       bestShip = i;
  1628.       bestMissile = -1;
  1629.     }
  1630.       }
  1631.  
  1632.       // check against missiles
  1633.       for (int j = 0; j < MAXMISSILES; j++) {
  1634.     hitDist = s->laserHitMissile(r, j);
  1635.     if (hitDist >= 0.0 && hitDist < bestDist) {
  1636.       bestDist = hitDist;
  1637.       bestShip = i;
  1638.       bestMissile = j;
  1639.     }
  1640.       }
  1641.     }
  1642.  
  1643.     // check against asteroids
  1644.     for (i = 0; i < numberAsteroids(); i++) {
  1645.       hitDist = laserHitAsteroid(r, i);
  1646.       if (hitDist >= 0.0 && hitDist <= bestDist) {
  1647.         bestDist = hitDist;
  1648.         bestAsteroid = i;
  1649.       }
  1650.     }
  1651.  
  1652.     if (bestAsteroid != -1) {
  1653.       // do nothing
  1654.     }
  1655.     else if (bestShip != -1) {
  1656.       ShipObject* s = getPlayer(bestShip);
  1657.  
  1658.       SwHitMessage hm;
  1659.       hm.hitter = id();
  1660.       hm.victim = s->id();
  1661.       hm.missileNum = bestMissile;
  1662.       hm.energy = t * laserPower();
  1663.       hm.position[0] = r.o[0] + bestDist * r.d[0];
  1664.       hm.position[1] = r.o[1] + bestDist * r.d[1];
  1665.       hm.position[2] = r.o[2] + bestDist * r.d[2];
  1666.       hm.velocityChange[0] = 0.0;
  1667.       hm.velocityChange[1] = 0.0;
  1668.       hm.velocityChange[2] = 0.0;
  1669.  
  1670.       if (bestMissile == -1) {
  1671.     // find local direction of impact
  1672.     SbVec3f hitPos(hm.position[0], hm.position[1], hm.position[2]);
  1673.     s->findLocalPosition(hitPos, hitPos);
  1674.     hm.position[0] = hitPos[0] / (SHIELDSIZE * sdx);
  1675.     hm.position[1] = hitPos[1] / (SHIELDSIZE * sdy);
  1676.     hm.position[2] = hitPos[2] / (SHIELDSIZE * sdz);
  1677.     s->hitShield(hm);
  1678.       }
  1679.       else {
  1680.     s->explodeMissile(hm);
  1681.       }
  1682.     }
  1683.  
  1684.     // set length of beam
  1685.     info.beamLength = bestDist;
  1686.     for (i = 1; i <= 9; i += 2)
  1687.       laserBeamPoints[i][2] = -info.beamLength;
  1688.     beamCoord->point.setValues(0, 10, laserBeamPoints);
  1689.  
  1690.     // heat up laser
  1691.     laserTemp += 1.5 * (t / 1.0);
  1692.  
  1693.     // decrement time left to fire laser
  1694.     if ((laserTime -= dt) <= 0.0) {        // out of time
  1695.       info.firingLaser = FALSE;            //  not firing
  1696.       laserSwitch->whichChild = SO_SWITCH_NONE;    //  turn beam off
  1697.     }
  1698.   }
  1699.  
  1700.   if (laserTempChanged) laserChanged();
  1701. }
  1702.  
  1703. void            ShipObject::advanceTurn(const SbVec3f& t, SbVec3f& v,
  1704.                         float maxAcc, float dt) const
  1705. {
  1706.   float da = dt * maxAcc;            // max angle we can turn
  1707.   if (da >= M_PI || t.dot(v) >= cos(da)) {    // < max or max to big
  1708.     v = t;                    //  reached the target
  1709.   }
  1710.   else {                    // can't reach target
  1711.     SbRotation turn(v.cross(t), da);        //  make maximum rotation
  1712.     SbMatrix m;
  1713.     turn.getValue(m);                //  make rotation matrix
  1714.     m.multDirMatrix(v, v);            //  rotate towards target
  1715.   }
  1716. }
  1717.  
  1718. void            ShipObject::findWorldDirection(const SbVec3f& in,
  1719.                             SbVec3f& out) const
  1720. {
  1721.   rotateMatrix.multDirMatrix(in, out);
  1722. }
  1723.  
  1724. void            ShipObject::findWorldPosition(const SbVec3f& in,
  1725.                             SbVec3f& out) const
  1726. {
  1727.   rotateMatrix.multVecMatrix(in, out);
  1728.   out += position();
  1729. }
  1730.  
  1731. void            ShipObject::findLocalDirection(const SbVec3f& in,
  1732.                             SbVec3f& out) const
  1733. {
  1734.   rotateMatrix.multMatrixVec(in, out);
  1735. }
  1736.  
  1737. void            ShipObject::findLocalPosition(const SbVec3f& in,
  1738.                             SbVec3f& out) const
  1739. {
  1740.   out = in;
  1741.   out -= position();
  1742.   rotateMatrix.multMatrixVec(out, out);
  1743. }
  1744.  
  1745. void            ShipObject::findLocalPosition(float p[3],
  1746.                             SbVec3f& out) const
  1747. {
  1748.   out.setValue(p);
  1749.   out -= position();
  1750.   rotateMatrix.multMatrixVec(out, out);
  1751. }
  1752.  
  1753. SoSeparator*        ShipObject::makeLaserTurret()
  1754. {
  1755.   SoSeparator* turretRoot = new SoSeparator;    // beam and turret under here
  1756.  
  1757.   // build transformation for entire turret
  1758.   turretRoot->addChild(laserLocation);
  1759.   turretRoot->addChild(laserOrientation);
  1760.  
  1761.   // build laser turret
  1762.   SoSeparator* turretStuff = new SoSeparator;    // turret under here
  1763.   turretRoot->addChild(turretStuff);
  1764.   turretStuff->setGLRenderCaching(TRUE);    // turret never changes
  1765.  
  1766.   SoComplexity* turretCmplx = new SoComplexity;    // not too complex turret
  1767.   turretStuff->addChild(turretCmplx);
  1768.   turretCmplx->type = SoComplexity::OBJECT_SPACE;
  1769.   turretCmplx->value = 0.1;
  1770.  
  1771.   SoMaterial* turretMaterial = new SoMaterial;    // turret material
  1772.   turretStuff->addChild(turretMaterial);
  1773.  
  1774.   SoSphere* turret = new SoSphere;        // turret is a sphere
  1775.   turretStuff->addChild(turret);
  1776.   turret->radius = TURRETRADIUS;
  1777.  
  1778.   SoTranslation* barrelPos = new SoTranslation;    // barrel is offset
  1779.   turretStuff->addChild(barrelPos);
  1780.   barrelPos->translation.setValue(0.0, 0.0, -TURRETRADIUS);
  1781.  
  1782.   SoRotation* barrelRotation = new SoRotation;    // and points along z-axis
  1783.   turretStuff->addChild(barrelRotation);
  1784.   barrelRotation->rotation.setValue(SbRotation(SbVec3f(0.0, 1.0, 0.0),
  1785.                             straightAhead));
  1786.  
  1787.   SoMaterial* barrelMaterial = new SoMaterial;    // barrel material
  1788.   turretStuff->addChild(barrelMaterial);
  1789.   barrelMaterial->diffuseColor.setValue(0.2, 0.2, 0.2);
  1790.  
  1791.   SoCylinder* barrel = new SoCylinder;        // barrel is a cylinder
  1792.   turretStuff->addChild(barrel);
  1793.   barrel->radius = BEAMRADIUS;
  1794.   barrel->height = TURRETRADIUS / 2.0;        // half turret radius
  1795.  
  1796.   return turretRoot;
  1797. }
  1798.  
  1799. SoSeparator*        ShipObject::makeLaserBeam()
  1800. {
  1801.   // build laser beam
  1802.   SoSeparator* laserBeam = new SoSeparator;    // all beam stuff under here
  1803.  
  1804.   // set beam transformation
  1805.   laserBeam->addChild(laserLocation);
  1806.   laserBeam->addChild(laserOrientation);
  1807.  
  1808.   SoSeparator* beamStuff = new SoSeparator;
  1809.   laserBeam->addChild(beamStuff);
  1810.   beamStuff->setGLRenderCaching(TRUE);        // beam never changes
  1811.  
  1812.   SoLightModel* beamLight = new SoLightModel;    // laser beam is it's own light
  1813.   beamStuff->addChild(beamLight);
  1814.   beamLight->model = SoLightModel::BASE_COLOR;
  1815.  
  1816.   SoMaterial* beamMaterial = new SoMaterial;    // laser beam is bright red
  1817.   beamStuff->addChild(beamMaterial);
  1818.   beamMaterial->diffuseColor.setValue(1.0, 0.0, 0.0);
  1819.  
  1820.   beamStuff->addChild(beamCoord = new SoCoordinate3);
  1821.   for (int i = 0; i <= 4; i++) {
  1822.     laserBeamPoints[2*i][0] = BEAMRADIUS * cos(M_PI * (float)(i%4) / 2.0);
  1823.     laserBeamPoints[2*i][1] = BEAMRADIUS * sin(M_PI * (float)(i%4) / 2.0);
  1824.     laserBeamPoints[2*i][2] = -3.0 * TURRETRADIUS / 2.0;
  1825.     laserBeamPoints[2*i+1][0] = BEAMRADIUS * cos(M_PI * (float)(i%4) / 2.0);
  1826.     laserBeamPoints[2*i+1][1] = BEAMRADIUS * sin(M_PI * (float)(i%4) / 2.0);
  1827.     laserBeamPoints[2*i+1][2] = -LASERRANGE;
  1828.   }
  1829.   beamCoord->point.setValues(0, 10, laserBeamPoints);
  1830.  
  1831.   SoTriangleStripSet* beam = new SoTriangleStripSet;
  1832.   beamStuff->addChild(beam);
  1833.   beam->startIndex = 0;
  1834.   beam->numVertices = 10;
  1835.  
  1836.   return laserBeam;
  1837. }
  1838.  
  1839. SoSeparator*        ShipObject::makeShields()
  1840. {
  1841.   SoSeparator* hitsAll = new SoSeparator;
  1842.  
  1843.   // hits makes their own light
  1844.   SoLightModel* hitLight = new SoLightModel;
  1845.   hitsAll->addChild(hitLight);
  1846.   hitLight->model = SoLightModel::BASE_COLOR;
  1847.  
  1848.   // hit material changes per vertex
  1849.   SoMaterialBinding* hitBinding = new SoMaterialBinding;
  1850.   hitsAll->addChild(hitBinding);
  1851.   hitBinding->value = SoMaterialBinding::PER_VERTEX;
  1852.  
  1853.   // hits glow greenish
  1854.   SoMaterial* hitColor = new SoMaterial;
  1855.   hitsAll->addChild(hitColor);
  1856.   hitColor->diffuseColor.setValue(0.25, 1.0, 0.25);
  1857.  
  1858.   // all hits have the same geometry
  1859.   SoCoordinate3* hitPoints = new SoCoordinate3;
  1860.   hitsAll->addChild(hitPoints);
  1861.   hitPoints->point.insertSpace(0, HITPOINTS);
  1862.   SbVec3f* points = hitPoints->point.startEditing();
  1863.   for (int c = 0, i = -HITRES; i <= HITRES; i++)
  1864.     for (int j = -HITRES; j <= HITRES; c++, j++) {
  1865.       int k = (abs(i) > abs(j) ? abs(i) : abs(j));
  1866.       points[c][0] = sin(HITSPREAD * (float)i / HITRES) *
  1867.             cos(HITSPREAD * (float)j / HITRES);
  1868.       points[c][1] = cos(HITSPREAD * (float)i / HITRES) *
  1869.             sin(HITSPREAD * (float)j / HITRES);
  1870.       points[c][2] = -cos(HITSPREAD * (float)k / HITRES);
  1871.       points[c].normalize();
  1872.     }
  1873.   hitPoints->point.finishEditing();
  1874.  
  1875.   // make shape (we'll reference it for each hit)
  1876.   SoQuadMesh* hitGeometry = new SoQuadMesh;
  1877.   hitGeometry->startIndex = 0;
  1878.   hitGeometry->verticesPerColumn = HITSIZE;
  1879.   hitGeometry->verticesPerRow = HITSIZE;
  1880.  
  1881.   // make individual hits
  1882.   for (i = 0; i < MAXHITS; i++) {
  1883.     hitsAll->addChild(hitSwitch[i] = new SoSwitch);
  1884.     SoSeparator* hitStuff = new SoSeparator;
  1885.     hitSwitch[i]->addChild(hitStuff);
  1886.     hitStuff->addChild(hitMaterial[i] = new SoMaterial);
  1887.     hitStuff->addChild(hitTransform[i] = new SoTransform);
  1888.     hitStuff->addChild(hitGeometry);
  1889.  
  1890.     hitTransform[i]->translation.setValue(sx, sy, sz);
  1891.     hitTransform[i]->scaleFactor.setValue(SHIELDSIZE * sdx,
  1892.                         SHIELDSIZE * sdy,
  1893.                         SHIELDSIZE * sdz);
  1894.     hitMaterial[i]->diffuseColor.setIgnored(TRUE);
  1895.     hitMaterial[i]->transparency.insertSpace(0, HITPOINTS);
  1896.   }
  1897.  
  1898.   return hitsAll;
  1899. }
  1900.